package com.ruoyi.common.flowable;

import cn.hutool.core.collection.ListUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.extra.spring.SpringUtil;
import cn.hutool.system.SystemUtil;
import com.ruoyi.common.core.domain.BusinessLogInfo;
import com.ruoyi.common.core.domain.entity.SysUser;
import com.ruoyi.common.core.mapper.SysUserMapper;
import com.ruoyi.common.utils.BaseSmsSend;
import com.ruoyi.common.utils.SmsFactory;
import com.ruoyi.common.utils.spring.SpringUtils;
import com.ruoyi.mapper.ActHiProcinstMapper;
import com.ruoyi.service.*;
import com.ruoyi.vo.FlowTaskVO;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.flowable.bpmn.model.Process;
import org.flowable.bpmn.model.*;
import org.flowable.common.engine.api.delegate.event.FlowableEvent;
import org.flowable.common.engine.api.delegate.event.FlowableEventListener;
import org.flowable.common.engine.impl.cfg.TransactionState;
import org.flowable.common.engine.impl.event.FlowableEntityEventImpl;
import org.flowable.engine.HistoryService;
import org.flowable.engine.RepositoryService;
import org.flowable.engine.RuntimeService;
import org.flowable.engine.TaskService;
import org.flowable.engine.task.Comment;
import org.flowable.task.api.Task;
import org.flowable.task.service.impl.persistence.entity.TaskEntity;
import org.springframework.stereotype.Component;
import java.io.UnsupportedEncodingException;
import java.util.*;

/**
 * 任务节点前置监听处理类
 *
 * @author: Lu Yang
 * @create: 2019-05-04 20:51
 **/
@Component
public class TaskCreatedListener implements FlowableEventListener {
    private static Log log = LogFactory.getLog(TaskCreatedListener.class);

    public FlowInstanceExtServiceImpl getFlowInstanceExtServiceImpl() {
        return SpringUtils.getBean(FlowInstanceExtServiceImpl.class);
    }

    public SysUserGroupServiceImpl getSysUserGroupServiceImpl() {
        return SpringUtils.getBean(SysUserGroupServiceImpl.class);
    }

    public static SysUserServiceImpl getSysUserService() {
        return SpringUtils.getBean(SysUserServiceImpl.class);
    }

    public static TaskCandidateUser getTaskCandidateUser() {
        return SpringUtils.getBean(TaskCandidateUser.class);
    }

    public static SysUserMapper getSysUserMapper() {
        return SpringUtils.getBean(SysUserMapper.class);
    }

    public static TaskService getTaskService() {
        return SpringUtils.getBean(TaskService.class);
    }

    public static RuntimeService getRuntimeService() {
        return SpringUtils.getBean(RuntimeService.class);
    }

    public static RepositoryService getRepositoryService() {
        return SpringUtils.getBean(RepositoryService.class);
    }

    public static HistoryService getHistoryService() {
        return SpringUtils.getBean(HistoryService.class);
    }

    public static ActHiProcinstMapper getActHiProcinstMapper() {
        return SpringUtils.getBean(ActHiProcinstMapper.class);
    }

    /**寻找候选人*/
    private List<String> getCandidateUserIdList(String taskId) {
        Task task = getTaskService().createTaskQuery().taskId(taskId).singleResult();
        List<Process> processes = getRepositoryService().getBpmnModel(task.getProcessDefinitionId()).getProcesses();
        if (CollectionUtils.isEmpty(processes)) {
            return new ArrayList<>();
        }
        TaskCandidateUser taskCandidateUser = getTaskCandidateUser();
        UserTask userTask = getCurrentUserTask(processes,task);
        List<String> candidateUserIdList = taskCandidateUser.getCandidateUserIdListOriginal( userTask, task);
        return candidateUserIdList;
    }

    /**寻找候选人*/
    private List<String> setAssignee(String taskId, List<String> userIdList, String lastAuditUserId) {
        if(userIdList.contains(lastAuditUserId)){
            getTaskService().addCandidateUser(taskId, lastAuditUserId);
            return ListUtil.of(lastAuditUserId);
        }else{
            Task task = getTaskService().createTaskQuery().taskId(taskId).singleResult();
            Map vars = getVariables(task);
            String candidatesUserId = (String) vars.get("candidatesUserId");
            if(StrUtil.isNotBlank(candidatesUserId)){
                getTaskService().addCandidateUser(taskId, candidatesUserId);
                return Arrays.asList(candidatesUserId.split(","));
            }else{
                List<Process> processes = getRepositoryService().getBpmnModel(task.getProcessDefinitionId()).getProcesses();
                TaskCandidateUser taskCandidateUser = getTaskCandidateUser();
                UserTask userTask = getCurrentUserTask(processes,task);
                List<String> candidateUserIdList = taskCandidateUser.getCandidateUserIdListOriginal( userTask, task);
                for (String userId : candidateUserIdList) {
                    getTaskService().addCandidateUser(task.getId(), userId);
                }
            }
        }
        return new ArrayList<>();
    }

    /**寻找当前UserTask*/
    private UserTask getCurrentUserTask(List<Process> processes, Task task) {
        for (Process process : processes) {
            Collection<FlowElement> flowElements = process.getFlowElements();
            if (CollectionUtils.isEmpty(flowElements)) {
                continue;
            }
            for (FlowElement flowElement : flowElements) {
                if (!(flowElement instanceof UserTask)) {
                    continue;
                }
                UserTask userTask = (UserTask) flowElement;
                if (!userTask.getId().equals(task.getTaskDefinitionKey())) {
                    continue;
                }
                return userTask;
            }
        }
        return null;
    }

    private void sendWxMsgWaitAudit(Task task, String userId) throws UnsupportedEncodingException {
        SysUser sysUser = getSysUserService().getById(userId);
        BaseSmsSend sender = SmsFactory.getSender();
        String phone = sysUser.getPhonenumber();
        if(SystemUtil.getOsInfo().isWindows()){
            phone = "18392447939";
        }
        sender.send(phone, "您有新的待审批任务，请及时处理！");
         /*for(SysUser user : userList){
           if (user.getOpenId() == null) {
                log.info("用户Openid 不存在");
                continue;
            }
            FlowTaskDto taskDto = getActHiProcinstMapper().getByProcInsId(task.getProcessInstanceId());
            if (taskDto == null) {
                log.info("taskDto is null ");
                return;
            }
            /*String openId = user.getOpenId();
            String templateId = Enum.WX_TEMPLATE.AUDIT_WAIT.getCode();
            String url = "https://kt.beitone.com/mobile/audit/detail?id=" + task.getId() + "&menu=waitToDoList";
            Map<String, Data> data = new HashMap<>();
            data.put("first", new Data(DateUtil.now(), "#173177"));
            data.put("keyword1", new Data(taskDto.getStartUserName(), "#173177"));
            data.put("keyword2", new Data(taskDto.getBusinessName(), "#173177"));
            data.put("remark", new Data("请您尽快审批哦~", "#173177"));
            wxSend(openId, templateId, url, data);
        }*/
    }
    @Override
    public void onEvent(FlowableEvent event) {
        // 当前节点任务实体
        TaskEntity taskEntity;
        try {
            taskEntity = (TaskEntity) ((FlowableEntityEventImpl) event).getEntity();
        }catch (Exception e){
            return;
        }

        log.info("TaskCreatedListener start taskId= " + taskEntity.getId());
        Task task = getTaskService().createTaskQuery().taskId(taskEntity.getId()).singleResult();
        Map vars = getTaskVars(task);
        String lastAuditUserId = (String) vars.get("lastAuditUserId");
        List<String> userIdList = getCandidateUserIdList(taskEntity.getId()); //获取当前创建节点的候选人
        List<String> realUserIdList = new ArrayList<>();
        if (taskEntity.getAssignee() == null || "null".equals(taskEntity.getAssignee())) {
            realUserIdList = setAssignee(taskEntity.getId(), userIdList,lastAuditUserId); //可能是下一个节点的候选人
        }
        getFlowInstanceExtServiceImpl().updateExt(taskEntity.getProcessInstanceId(), taskEntity,realUserIdList);
        if (lastAuditUserId != null && userIdList.contains(lastAuditUserId)) {
            FlowTaskVO flowTaskVo = new FlowTaskVO(taskEntity.getId(),"审批人与前一个处理人是同一人，自动审批通过");//自动审核通过
            flowTaskVo.setCandidatesUserId((String) vars.get("candidatesUserId"));
            BusinessLogInfoServiceImpl businessLogService = SpringUtil.getBean(BusinessLogInfoServiceImpl.class);
            BusinessLogInfo log = businessLogService.findLastByProcInstId(task.getProcessInstanceId());
            if(log != null){
                log.setId(null);
                log.setCreateTime(new Date());
                log.setContent(log.getContent() + "，审批人与前一个处理人是同一人，自动审批通过");
                log.insert();
            }

            SpringUtil.getBean(FlowTaskServiceImpl.class).complete(flowTaskVo);
        }else{
            try {
                sendWxMsg(taskEntity,userIdList);
            } catch (UnsupportedEncodingException e) {
                throw new RuntimeException(e);
            }
        }

        log.info("TaskCreatedListener end taskId= " + taskEntity.getId());
    }

    private Map getTaskVars(Task task){
        Map runtimeVars = getRuntimeService().getVariables(task.getProcessInstanceId());
        Map vars = task.getProcessVariables();
        if(vars.isEmpty()){
            vars.putAll(runtimeVars);
        }
        vars.putAll(task.getTaskLocalVariables());
        return vars;
    }

    private void sendWxMsg(TaskEntity taskEntity, List<String> userIdList) throws UnsupportedEncodingException {
        List<Comment> commentListAll = SpringUtils.getBean(TaskService.class).getProcessInstanceComments(taskEntity.getProcessInstanceId());
        log.info("开始发送微信消息，commentListAll.size = " + commentListAll.size());
        String type = commentListAll.get(0).getType();
        //boolean isStartNode = isFirstNode(taskEntity);
        log.info("userIdList = " + userIdList.size());
        log.info("type = " + type);
        //log.info("isStartNode = " + isStartNode);
        for (int i = 0; i < userIdList.size(); i++) {
            String userId = userIdList.get(i);
            if (type.equals("1")) {
                log.info("发送待审核消息 sendWxMsgWaitAudit start");
                sendWxMsgWaitAudit(taskEntity, userId);
                log.info("发送待审核消息 sendWxMsgWaitAudit end");
            } else if (type.equals("2") || type.equals("3")) {
                /*if (isStartNode) { //退回到发起人
                    sendWxMsgFailAudit(taskEntity, userId, isStartNode);
                } else {
                    sendWxMsgFailAudit(taskEntity, userId, isStartNode);
                }*/
            }
        }
    }

    private boolean isFirstNode(TaskEntity task) {
        List<Process> processes = getRepositoryService().getBpmnModel(task.getProcessDefinitionId()).getProcesses();
        if (CollectionUtils.isEmpty(processes)){
            return false;
        }
        for (Process process : processes) {
            Collection<FlowElement> flowElements = process.getFlowElements();
            if (CollectionUtils.isEmpty(flowElements)){
                continue;
            }
            List<SequenceFlow> flows = ((StartEvent) process.getInitialFlowElement()).getOutgoingFlows();
            for (int i = 0; i < flows.size(); i++) {
                UserTask firstUserTask = (UserTask) flows.get(i).getTargetFlowElement();
                return firstUserTask.getId().equals(task.getTaskDefinitionKey());
            }
        }
        return false;
    }

    /**获取第一个用户任务*/
    private UserTask getFirstUserTask(List<Process> processes) {
        for (Process process : processes) {
            Collection<FlowElement> flowElements = process.getFlowElements();
            if (CollectionUtils.isEmpty(flowElements)){
                return null;
            }
            List<SequenceFlow> flows = ((StartEvent) process.getInitialFlowElement()).getOutgoingFlows();
            for (int i = 0; i < flows.size(); i++) {
                UserTask firstUserTask = (UserTask) flows.get(i).getTargetFlowElement();
                return firstUserTask;
            }
        }
        return null;
    }

    public static Map<String, Object> getVariables(Task task) {
        Map runtimeVars = getRuntimeService().getVariables(task.getProcessInstanceId());
        Map vars = task.getProcessVariables();
        if(vars.isEmpty()){
            vars.putAll(runtimeVars);
        }
        vars.putAll(task.getTaskLocalVariables());
        return vars;
    }

    @Override
    public boolean isFailOnException() {
        return true;
    }

    @Override
    public boolean isFireOnTransactionLifecycleEvent() {
        return true;
    }

    @Override
    public String getOnTransaction() {
        return TransactionState.COMMITTED.name();
    }
}